﻿using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using System.IO;
using System.Net;
using System.Text;
using System.Web;
using System.Xml;

namespace VivikaiLabs.Connectors
{
    public class SolrClient
    {
        string solr_url_base = "http://localhost:8983/solr/";
        string solr_url_update;
        string solr_url_query;

        #region Constructor
        
        public SolrClient()
        {
            solr_url_update = Path.Combine(solr_url_base, "update");
            solr_url_query = Path.Combine(solr_url_base, "select");
        }

        public SolrClient(string baseSolrUrl)
        {
            solr_url_base = baseSolrUrl;
            solr_url_update = Path.Combine(solr_url_base, "update");
            solr_url_query = Path.Combine(solr_url_base, "select");
        }

        #endregion
        public void IndexDocuments(string[] files, Encoding encoding)
        {
            foreach (string file in files)
            {
                if (File.Exists(file))
                {
                    using (TextReader reader = new StreamReader(file, Encoding.UTF8))
                    {
                        PostData(reader);
                    }
                }
            }
            Commit();
        }

        public void IndexDocuments(string directory, string searchPattern, SearchOption searchOption, Encoding fileEncoding)
        {
            if (Directory.Exists(directory))
            {
                string[] files = Directory.GetFiles(directory, searchPattern, searchOption);
                IndexDocuments(files, fileEncoding);
            }
        }

        public XmlReader SearchXmlReader(string keyword)
        {
            XmlReader responseReader = new XmlTextReader(Search(keyword));

            return responseReader;
        }

        public XmlDocument SearchXmlDocument(string keyword)
        {
            using (XmlReader reader = SearchXmlReader(keyword))
            {
                XmlDocument doc = new XmlDocument();
                doc.Load(reader);
                return doc;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="keyword"></param>
        /// <returns></returns>
        public SolrResult SearchObject(string keyword)
        {
            int status = 0;
            int queryTime = 0;
            Hashtable parameters = new Hashtable();
            dynamic[] results;

            XmlDocument doc = SearchXmlDocument(keyword);

            XmlNodeList headerNodes = doc.ChildNodes[1].ChildNodes[0].ChildNodes;
            foreach (XmlNode node in headerNodes)
            {
                switch (node.Attributes["name"].Value.ToLower())
                {
                    case "status":
                        int.TryParse(node.InnerText, out status);
                        break;
                    case "qtime":
                        int.TryParse(node.InnerText, out queryTime);
                        break;
                    case "params":
                        foreach (XmlNode p in node.ChildNodes)
                        {
                            string key = p.Attributes["name"].Value;
                            string value = p.InnerText;

                            parameters.Add(key, value);
                        }
                        break;
                }
            }

            int resultCount;
            int.TryParse(doc.ChildNodes[1].ChildNodes[1].Attributes["numFound"].Value, out resultCount);
            results = new dynamic[resultCount];
            XmlNodeList bodyNodes = doc.ChildNodes[1].ChildNodes[1].ChildNodes;
            for(int i = 0; i < bodyNodes.Count; i++)
            {
                dynamic document = new Document();
                foreach (XmlNode childNode in bodyNodes[i].ChildNodes)
                {
                    document[childNode.Attributes["name"].Value] = childNode.InnerXml;
                }
                results[i] = document;
            }

            return new SolrResult(status, queryTime, parameters, results);
        }

        public string SearchJson(string keyword)
        {
            Stream stream = Search(keyword, SearchResultFormat.Json);
            using (StreamReader reader = new StreamReader(stream))
            {
                string result = reader.ReadToEnd();
                return result;
            }
        }

        public void DeleteById(string id)
        {
            if (!string.IsNullOrEmpty(id))
            {
                PostData(new StringReader(string.Format("<delete><id>{0}</id></delete>", id.Trim())));
                Commit();
            }
        }

        public void DeleteByQuery(string query)
        {
            if (!string.IsNullOrEmpty(query))
            {
                PostData(new StringReader(string.Format("<delete><query>{0}</query></delete>", query.Trim())));
                Commit();
            }
        }

        public void Optimize()
        {
            PostData(new StringReader("<optimize />"));
        }

        private Stream Search(string keyword, SearchResultFormat searchResultFormat = SearchResultFormat.Xml)
        {
            if (string.IsNullOrEmpty(keyword))
            {
                return null;
            }

            string querystring = HttpUtility.UrlEncode(keyword, Encoding.UTF8);
            querystring = solr_url_query + "?q=" + querystring;

            if (searchResultFormat == SearchResultFormat.Json)
            {
                querystring += "&wt=json";
            }

            WebRequest request = HttpWebRequest.Create(querystring);

            request.Method = WebRequestMethods.Http.Get;
            WebResponse response = request.GetResponse();

            return response.GetResponseStream();
        }

        private void Commit()
        {
           PostData(new StringReader("<commit />"));
        }

        private void PostData(TextReader reader)
        {
            PostData(reader, Encoding.UTF8);
        }

        private void PostData(TextReader reader, Encoding encoding)
        {
            WebRequest request = HttpWebRequest.Create(solr_url_update);
            request.Method = WebRequestMethods.Http.Post;
            request.ContentType = "text/xml; charset=" + encoding.WebName;
            Stream stream = request.GetRequestStream();
            StreamWriter writer = new StreamWriter(request.GetRequestStream());
            Pipe(reader, writer);
            WebResponse response = request.GetResponse();

            TextReader responseReader = new StreamReader(response.GetResponseStream());

            Pipe(responseReader, System.Console.Out);
        }

        private static void Pipe(TextReader reader, TextWriter writer)
        {
            char[] buf = new char[1024];
            int read;
            while ((read = reader.Read(buf, 0, 1024)) > 0)
            {
                writer.Write(buf, 0, read);
            }

            writer.Flush();
        }
    }

    internal enum SearchResultFormat
    {
        Xml,
        Json,
        Object
    }

    public class SolrResult
    {
        private int _status = 0;
        private int _queryTime = 0;
        private Hashtable _parameters = new Hashtable();
        private dynamic[] _results;

        internal SolrResult(int status, int queryTime, Hashtable parameters, dynamic[] results)
        {
            _status = status;
            _queryTime = queryTime;
            _parameters = parameters;
            _results = results;
        }

        public int Status
        {
            get
            {
                return _status;
            }
        }

        public int QueryTime
        {
            get
            {
                return _queryTime;
            }
        }

        public Hashtable Parameters
        {
            get
            {
                return _parameters;
            }
        }

        public dynamic[] Results
        {
            get
            {
                return _results;
            }
        }
    }

    public class Document : DynamicObject
    {
        Dictionary<string, object>
          _dictionary = new Dictionary<string, object>();

        public object this[string name]
        {
            get
            {
                return _dictionary[name];
            }
            set
            {
                _dictionary[name] = value;
            }
        }

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            _dictionary[binder.Name] = value;
            return true;
        }

        public override bool TryGetMember(GetMemberBinder binder,
            out object result)
        {
            return _dictionary.TryGetValue(binder.Name, out result);
        }
    }
}